{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 请简述基于内容的推荐和基于协同过滤的推荐的基本原理，并指出二者的适用场景。（20分） "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1.基于内容的推荐：<br>\n",
    "    &emsp;&emsp;推荐给用户的item是基于过去的消费过的item，然后根据用户喜欢的item的属性进行推荐，无需考虑其他用户的行为。根据用户已选的item，计算出用户的兴趣爱好和item的特征之间的匹配程度，根据匹配程度进行排序然后推给用户。<br>\n",
    "2.基于协同过滤的推荐：<br>\n",
    "2.1基于用户的协同过滤原理：<br>\n",
    "    &emsp;&emsp;首先分析各个用户对item的评价（通过浏览记录或者购买记录等）；然后依据用户对item的评价计算得出所有用户之间的相似度；再选出与当前用户最相似的N个用户，将这N个用户评价最高并且当前用户又没有浏览过的item推荐给当前用户。<br>\n",
    "2.2基于物品的协同过滤原理：<br>\n",
    "    &emsp;&emsp;首先分析所有用户浏览过的item记录，然后根据浏览记录分析得出所有item之间的相似度，然后根据测试用户评价高的item，找出与之相似度最高的N个item推荐给测试用户。<br>    \n",
    "2.3基于矩阵分解的协同过滤的原理：<br>\n",
    "    &emsp;&emsp;当用户、物品较多的时候，基于用户和物品的协同过滤算法存在稀疏性的问题，不能做到实时的对用户进行推荐。基于模型的协同过滤算法有效的解决了这一问题，矩阵分解是基于模型的协同过滤算法中的一种。将矩阵分解应用于协同过滤算法可以提取物品、用户的隐式特征，发现一些不是显而易见的特征，在一定程度上解决了稀疏问题。<br>\n",
    "    &emsp;&emsp;矩阵分解就是把原来的大矩阵，近似的分解成小矩阵的乘积，在实际推荐计算时不再使用大矩阵，而是使用分解得到的两个小矩阵具体来说就是，假设用户物品的评分矩阵A是m乘n维，即一共有m个用户，n个物品。通过一套算法转化为两个矩阵U和V,矩阵U的维度是m乘k，矩阵V的维度是n乘k。<br>\n",
    "    &emsp;&emsp;这两个矩阵的要求就是通过下面这个公式可以复原矩阵A:<br>\n",
    "    ![1001](http://qhx695ink.hb-bkt.clouddn.com/1001.jpg)<br>\n",
    "    &emsp;&emsp;类似这样的计算过程就是矩阵分解，常见的矩阵分解算法是SVD：就是把用户和物品都映射到一个K维空间上，这个k维空间不是直接看到的（通常称为隐因子），每一个物品都得到一个向量q，每一个用户也得到一个向量p。对于物品，与它对应的向量q中的元素，有正有负，代表着这个物品背后暗藏的一些用户关注的因素，对于用户，与它对应的向量p中的元素，也有正有负，代表这个用户在若干因素上的偏好。物品被关注的因素和用户偏好的因素，它们的数量和意义是一致的，就是我们在矩阵分解之处人为指定的k，如何得到每一个用户，每一个物品的k维向量，这就需要建立一个模型按照机器学习思想，一般考虑两个要素：1)、损失函数 2)、优化算法<br>\n",
    "    &emsp;&emsp;SVD的目标函数如下：<br>\n",
    "    ![1002](http://qhx695ink.hb-bkt.clouddn.com/1002.jpg)<br>\n",
    "    &emsp;&emsp;损失函数由两部分组成，前一部分是控制模型的偏差，用分解后的矩阵预测分数，要和实际用户评分之间误差越小越好；后一部分是正则控制模型的复杂度，得到的隐因子向量要越简单越好。整个SVD的学习过程就是：<br>\n",
    "      &emsp;&emsp;1）准备好用户物品的评分矩阵，每一条评分数据看作是一条训练样本<br>\n",
    "      &emsp;&emsp;2）给分解后的U矩阵和V矩阵随机初始化元素值<br>\n",
    "      &emsp;&emsp;3）用U和V计算预测后的分数<br>\n",
    "      &emsp;&emsp;4)  计算预测的分数和实际的分数误差<br>\n",
    "      &emsp;&emsp;5）按照梯度下降的方向更行U和V中的元素值<br>\n",
    "      &emsp;&emsp;6）重复步骤3到5，直到达到停止条件<br>\n",
    "    &emsp;&emsp;得到分解后的矩阵之后，实质上就是得到每个用户和每个物品的隐因子向量，拿着这个向量再做推荐计算更加简单，推荐分数就是物品和用户两个向量的点积。 <br>\n",
    "    \n",
    "   &emsp;&emsp;对于推荐系统来说存在两大场景即评分预测与Top-N推荐。评分预测场景主要用于评价网站，比如用户给自己看过的电影评多少分，或者用户给自己看过的书籍评价多少分。其中基于协同过滤的推荐技术主要应用于该场景。Top-N推荐场景主要用于购物网站或者一般拿不到显式评分信息的网站，即通过用户的隐式反馈信息来给用户推荐一个可能感兴趣的列表以供其参考。其中该场景为排序任务，因此需要排序模型来对其建模基于内容的推荐技术主要应用于该场景。<br>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 请分别给出一个基于用户的协同过滤和基于物品的协同过滤的典型应用场景。（20分）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;UserCF算法主要有两步：1）找到和目标用户兴趣相似的用户集合2）找到这个集合中的用户喜欢的，且目标用户没有听说过的物品推荐给目标用户。<br>\n",
    "&emsp;&emsp;ItemCF算法主要有两步：1）计算物品之间的相似度。2）根据物品的相似度和用户的历史行为给用户生成推荐列表。<br>\n",
    "&emsp;&emsp;Item CF是利用物品间的相似性来推荐的，所以假如用户的数量远远超过物品的数量，那么可以考虑使用Item CF，比如购物网站，因其物品的数据相对稳定，因此计算物品的相似度时不但计算量较小，而且不必频繁更新;User CF更适合做新闻、博客或者微内容的推荐系统，因为其内容更新频率非常高，特别是在社交网络中，User CF是一个更好的选择，可以增加用户对推荐解释的信服程度。<br>\n",
    "&emsp;&emsp;而在一个非社交网络的网站中，比如给某个用户推荐一本书，系统给出的解释是某某和你有相似兴趣的人也看了这本书，这很难让用户信服，因为用户可能根本不认识那个人;但假如给出的理由是因为这本书和你以前看的某本书相似，这样解释相对合理，用户可能就会采纳你的推荐。<br>\n",
    "&emsp;&emsp;UserCF是推荐用户所在兴趣小组中的热点，更注重社会化，而ItemCF则是根据用户历史行为推荐相似物品，更注重个性化。所以UserCF一般用在新闻类网站中，如Digg,而ItemCF则用在其他非新闻类网站中，如Amazon,hulu等等。<br>\n",
    "&emsp;&emsp;因为在新闻类网站中，用户的兴趣爱好往往比较粗粒度，很少会有用户说只看某个话题的新闻，往往某个话题也不是天天会有新闻的。个性化新闻推荐更强调新闻热点，热门程度和时效性是个性化新闻推荐的重点，个性化是补充，所以UserCF给用户推荐和他有相同兴趣爱好的人关注的新闻，这样在保证了热点和时效性的同时，兼顾了个性化。另外一个原因是从技术上考虑的，作为一种物品，新闻的更新非常快，而且实时会有新的新闻出现，而如果使用ItemCF的话，需要维护一张物品之间相似度的表，实际工业界这表一般是一天一更新的，这在新闻领域是万万不能接受的。<br>\n",
    "&emsp;&emsp;但是，在图书，电子商务和电影网站等方面，ItemCF则能更好的发挥作用。因为在这些网站中，用户的兴趣爱好一般是比较固定的，而且相比于新闻网站更细腻。在这些网站中，个性化推荐一般是给用户推荐他自己领域的相关物品。另外，这些网站的物品数量更新速度不快，一天一次更新可以接受。而且在这些网站中，用户数量往往远远大于物品数量，从存储的角度来讲，UserCF需要消耗更大的空间复杂度，另外，ItemCF可以方便的提供推荐理由，增加用户对推荐系统的信任度，所以更适合这些网站。<br>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 将推荐电影数目改成20个，运行课程给的代码，比较三种协同过滤算法的性能，并和推荐数目为10的推荐结果比较。（60分）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "运行结果的分析：<br>  \n",
    "1.计算性能的比较  <br>\n",
    "&emsp;&emsp;在基于用户的协同过滤中，计算所有用户的相似性耗时208.4399秒，完成459个User的推荐, 共计耗时899.3862760066986秒<br>\n",
    "&emsp;&emsp;在基于物品的协同过滤中，计算943个Item两两之间的相似度共计耗时215.0979秒，完成459个User的推荐, 共计耗时557.8829436302185秒<br>\n",
    "&emsp;&emsp;在基于SVD的协同过滤中，完成了50个step的训练耗时879.4734秒，完成459个User的推荐, 共计耗时494.5466454029083秒<br>\n",
    "  \n",
    "&emsp;&emsp;从上面数据中可以看出，在计算性能上计算相似度的时间要比训练模型的时间远远的短，但是反而在推荐电影时基于矩阵分解的CF是训练了一个模型，然后用模型去进行预测，速度反而要比另外两个CF快，前两种CF方法，并没有训练模型，所以每次做推荐都要从头算一遍。<br>\n",
    "  \n",
    "2.评价指标的比较  <br>\n",
    "\n",
    "|评价指标/算法|基于User的CF|基于User的CF|基于Item的CF|基于Item的CF|基于SVD的CF|基于SVD的CF|\n",
    "|:---------:|:----:|:------:|:----:|:------:|:---:|:-----:|\n",
    "|推荐电影数目|   10  |   20   |   10 |    20  |  10 |   20  |\n",
    "|Precision |0.03725490196078431|0.03714596949891068|0.052505446623093685|0.05108932461873638|0.07777777777777778|0.08017429193899782|\n",
    "|Recall    |0.00855|0.01705|0.01205|0.02345|0.01785|0.0368|\n",
    "|Coverage  |0.2735949098621421|0.45493107104984093|0.8080593849416755|0.9353128313891834|0.08969696969696969|0.1387878787878788\n",
    "|rmse      |3.4330121066623134|3.4330121066623134|1.198515696861265|1.198515696861265|0.9256115716800145|0.9256115716800145|\n",
    "\n",
    "&emsp;&emsp;对于不同算法的评价指标来说，基于Item的CF在precision, recall, coverage, rmse四个评价指标上都优于基于User的CF，可见计算出的item之间的相似度比较稳定，而基于SVD的CF， 由于是采用了训练模型来推荐，所以效果上precision, recall, rmse指标比另外两个算法都要好，但在coverage比两外两个算法较差。<br>\n",
    "\n",
    "&emsp;&emsp;从推荐电影的数目上来说，rmse完全一样，precision也相差不大；相比于推荐10个电影，推荐20个电影的recall和coverage有明显的提升，这是因为这两个指标的公式中分子中有个参数是推荐item的数目，所以返回的样本多了，这两个指标自然就上升了，公式如下。<br>\n",
    "\n",
    "$$ Recall = \\frac{有用的商品数量 \\bigcap 推荐商品数量}{有用的商品数量} $$\n",
    "$$ Coverage = \\frac{|用户总数 \\times 推荐每一用户的商品数量|}{|商品总数|} $$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "hide_input": false,
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  },
  "nbTranslate": {
   "displayLangs": [
    "*"
   ],
   "hotkey": "alt-t",
   "langInMainMenu": true,
   "sourceLang": "en",
   "targetLang": "fr",
   "useGoogleTranslate": true
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
